/*

  xmunipack - detail panel

  Copyright © 1997-2011 F.Hroch (hroch@physics.muni.cz)

  This file is part of Munipack.

  Munipack is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  
  Munipack is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with Munipack.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "xmunipack.h"
#include <math.h>
#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/graphics.h>

#define STRIP_SIZE 20





// ---- MuniStrip  ---------------------------------------------

BEGIN_EVENT_TABLE(MuniStrip, wxPanel)
  EVT_PAINT(MuniStrip::OnPaint)
  EVT_SIZE(MuniStrip::OnSize)
//  EVT_IDLE(MuniStrip::OnIdle)
END_EVENT_TABLE()

MuniStrip::MuniStrip(wxWindow *w, const wxSize& s, const FitsItt& i,
		     const FitsPalette& l): 
wxPanel(w,wxID_ANY,wxDefaultPosition,s), itt(i), pal(l),
  strip_height(STRIP_SIZE), big_tic(8),small_tic(5)
{
  SetBackgroundStyle(wxBG_STYLE_CUSTOM);
  sf = wxFont(*wxNORMAL_FONT);
  Create();
}

void MuniStrip::SetItt(const FitsItt& i)
{
  itt = i;
  Create();
}

void MuniStrip::SetPalette(const FitsPalette& l)
{
  pal = l;
  Create();
}

void MuniStrip::OnPaint(wxPaintEvent& event)
{
  if( strip.IsOk() ) {
    wxAutoBufferedPaintDC dc(this);
    dc.DrawBitmap(strip,0,0);
  }
}

void MuniStrip::OnSize(wxSizeEvent& event)
{
  //  Create();

  wxPaintEvent ev;
  OnPaint(ev);
}

void MuniStrip::OnIdle(wxIdleEvent& event)
{
  //  Create();
  //  wxPaintEvent ev;
  //  OnPaint(ev);
}

void MuniStrip::Create()
{
  wxSize size = GetSize();

  wxImage itemp(size.GetWidth(),size.GetHeight(),false);

  strip = wxBitmap(itemp);
  wxMemoryDC dc(strip);
  //  dc.Clear();
  wxGraphicsContext *gc = wxGraphicsContext::Create(dc);

  if(gc) {

    gc->SetBrush(*wxTRANSPARENT_BRUSH);
    gc->SetFont(*wxSMALL_FONT,
		wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));

    // canvas rectangle
    gc->SetPen(*wxWHITE_PEN);
    gc->SetBrush(*wxWHITE_BRUSH);    

    // clear
    gc->SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
    gc->DrawRectangle(0,0,size.GetWidth(),size.GetHeight());

    // strip
    double dw = double(size.GetWidth())/double(pal.GetColors());
    int wt = int(dw + 1.5);
    for(int l = 0; l < pal.GetColors(); l++) {
      int ll = int(l*dw);
      wxColour c(pal.R(l),pal.G(l),pal.B(l));
      gc->SetPen(wxPen(c));
      gc->SetBrush(wxBrush(c));
      gc->DrawRectangle(ll,0,wt,strip_height);
    }

    // limits
    double xmin = itt.InvScale(0);
    double xmax = itt.InvScale(1/*pal.GetColors()*/);

    // tics 
    double tic = (xmax - xmin)/13.0;

    // rounding to only one place
    double p = log10(tic);
    double e = trunc(p); if( p < 0.0 ) e = e - 1.0; 
    double e10 = pow(10.0,e);
    tic = trunc(pow(10.0,p-e))*e10;
    //    wxLogDebug(_("%f %f %f %f %f %f"),xmin,xmax,p,e,e10,tic);


    // start to rounded position
    double x0 = trunc((xmin - tic)/e10)*e10;
    
    gc->SetPen(*wxBLACK_PEN);
    gc->SetBrush(*wxWHITE_BRUSH);
    gc->SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
    gc->SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));

    // tics
    double x = x0;
    while( x < xmax ) {
      //      double l = itt.Fscale(x)*dw;
      double ytic = fabs(fmod(x -(x0+tic),5.0*tic)) < 0.5 ? big_tic : small_tic;

      int l = int(256*itt.Scale(x)*dw);
      wxPoint2DDouble lines[] = { wxPoint2DDouble(l,strip_height), 
				  wxPoint2DDouble(l,strip_height+ytic) };
      gc->DrawLines(2,lines);

      x = x + tic;
    }

    // labels
    x = x0 + tic;
    while( x < xmax ) {

      wxString a;
      double tw,th,u,v,xoff;

      a.Printf(wxT("%g"),x);
      gc->GetTextExtent(a,&tw,&th,&u,&v);
      xoff = tw/2.0;
      int l = int(256*itt.Scale(x)*dw);
      gc->DrawText(a,l-xoff,strip_height+big_tic);
      x = x + 10.0*tic;
    }

    delete gc;
  }
  dc.SelectObjectAsSource(wxNullBitmap);
}


// ---- MuniCanvasMini ---------------------------------------------

MuniCanvasMini::MuniCanvasMini(wxWindow *w, int z, int s):
wxWindow(w,wxID_ANY),size(z),scale(s)
{
  SetBackgroundStyle(wxBG_STYLE_CUSTOM);
  
  Bind(wxEVT_PAINT,&MuniCanvasMini::OnPaint,this);
  Bind(wxEVT_UPDATE_UI,&MuniCanvasMini::OnUpdate,this);
}

wxSize MuniCanvasMini::DoGetBestSize() const
{
  int s = size*scale;
  return wxSize(s,s);
}

void MuniCanvasMini::SetImage(const wxBitmap& b) 
{ 
  image = b; 
}

void MuniCanvasMini::OnPaint(wxPaintEvent& event)
{
  // draw image bitmap
  wxAutoBufferedPaintDC dc(this);

  if( image.IsOk() )
    dc.DrawBitmap(image,0,0,false);
  else {
    dc.SetBackground(*wxBLACK_BRUSH);
    dc.Clear();
  }

  // dimensions
  int w = size*scale;
  int h = w;

  // draw decorations
  dc.SetBrush(*wxTRANSPARENT_BRUSH);
  dc.SetPen(*wxBLACK_PEN);
  dc.DrawRectangle(0,0,w,h);
  dc.SetPen(*wxWHITE_PEN);
  dc.DrawRectangle(1,1,w-2,h-2);
  
  // draw cross box
  int t = w/2 - scale/2 - 2;
  dc.SetBrush(*wxTRANSPARENT_BRUSH);
  
  dc.SetPen(*wxWHITE_PEN);
  dc.DrawRectangle(t-2,t-2,scale+4,scale+4);
  
  dc.SetPen(*wxBLACK_PEN);
  dc.DrawRectangle(t-1,t-1,scale+2,scale+2);
}

void MuniCanvasMini::OnUpdate(wxUpdateUIEvent& event)
{
  event.SetUpdateInterval(10);
  Refresh();
}



// ---- MuniDetail


MuniDetail::MuniDetail(wxWindow *w, wxWindowID id, const wxPoint& pos, 
		       const wxSize& size, MuniConfig *c):
  wxDialog(w,id,"Detail",pos,size,wxDEFAULT_DIALOG_STYLE), config(c)
{
  wxColour bc(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWFRAME));

  wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);

  // zoom
  wxStaticBoxSizer *zsizer = new wxStaticBoxSizer(wxVERTICAL,this);

  zoom = new MuniCanvasMini(this,config->detail_zoom,config->detail_scale);
  zsizer->Add(zoom,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL).Border(wxTOP|wxBOTTOM));

  wxArrayString sdetail;
  sdetail.Add(wxT("Image"));
  //  sdetail.Add(wxT("Profile 2D"));
  //  sdetail.Add(wxT("Profile 3D"));
  //  sdetail.Add(wxT("Contours"));

  wxChoice *dtvalue = new wxChoice(this,wxID_ANY,wxDefaultPosition,
				   wxDefaultSize,sdetail);  
  zsizer->Add(dtvalue,wxSizerFlags().Expand().Align(wxALIGN_BOTTOM));

  topsizer->Add(zsizer,wxSizerFlags().Expand().Border());


  // values
  wxStaticBoxSizer *vsizer = new wxStaticBoxSizer(wxVERTICAL,this,
						  wxT(" Value "));
  wxTextCtrl *value = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
			   wxDefaultPosition, wxDefaultSize, 
			   wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  value->SetBackgroundColour(bc);
  vsizer->Add(value,wxSizerFlags().Expand().Border());

  valchoice = new wxChoice(this,ID_VALTYPE,wxDefaultPosition,
			   wxDefaultSize,FitsValue::Label_str());
  vsizer->Add(valchoice,wxSizerFlags().Expand().Border());
  topsizer->Add(vsizer,wxSizerFlags().Expand().Border());

  // coordinates
  wxStaticBoxSizer *csizer = new wxStaticBoxSizer(wxVERTICAL,this,
						  wxT(" Coordinates "));

  wxFlexGridSizer *cgrid = new wxFlexGridSizer(2);
  cgrid->AddGrowableCol(1);

  wxStaticText *xlabel = new wxStaticText(this,wxID_ANY,wxEmptyString);
  cgrid->Add(xlabel,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  wxTextCtrl *xcoordinate = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
			   wxDefaultPosition, wxDefaultSize, 
			   wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  xcoordinate->SetBackgroundColour(bc);
  cgrid->Add(xcoordinate,wxSizerFlags().Expand());

  wxStaticText *ylabel = new wxStaticText(this,wxID_ANY,wxEmptyString);
  cgrid->Add(ylabel,wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
  wxTextCtrl *ycoordinate = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
			   wxDefaultPosition, wxDefaultSize, 
			   wxTE_RIGHT|wxTE_DONTWRAP|wxTE_READONLY);
  ycoordinate->SetBackgroundColour(bc);
  cgrid->Add(ycoordinate,wxSizerFlags().Expand());
  csizer->Add(cgrid,wxSizerFlags(1).Expand().Border());

  coochoice = new wxChoice(this,ID_COOTYPE,wxDefaultPosition,wxDefaultSize,
			   FitsCoo::Label_str());
  csizer->Add(coochoice,wxSizerFlags().Expand().Border());

  topsizer->Add(csizer,wxSizerFlags().Expand().Border());
  SetSizerAndFit(topsizer);


  Connect(wxEVT_CLOSE_WINDOW,wxCloseEventHandler(MuniDetail::OnClose));
  //  Connect(xEVT_SLEW,xSlewEventHandler(MuniDetail::OnMouseMotion));
  Connect(ID_COOTYPE,wxEVT_COMMAND_CHOICE_SELECTED,
	  wxCommandEventHandler(MuniDetail::OnChoiceCoo));
  Connect(ID_VALTYPE,wxEVT_COMMAND_CHOICE_SELECTED,
	  wxCommandEventHandler(MuniDetail::OnChoiceVal));

  Connect(value->GetId(),wxEVT_UPDATE_UI,
  	  wxUpdateUIEventHandler(MuniDetail::OnUpdateValue));
  Connect(xcoordinate->GetId(),wxEVT_UPDATE_UI,
  	  wxUpdateUIEventHandler(MuniDetail::OnUpdateXCoo));
  Connect(ycoordinate->GetId(),wxEVT_UPDATE_UI,
  	  wxUpdateUIEventHandler(MuniDetail::OnUpdateYCoo));
  Connect(xlabel->GetId(),wxEVT_UPDATE_UI,
  	  wxUpdateUIEventHandler(MuniDetail::OnUpdateXlabel));
  Connect(ylabel->GetId(),wxEVT_UPDATE_UI,
  	  wxUpdateUIEventHandler(MuniDetail::OnUpdateYlabel));

}

void MuniDetail::OnClose(wxCloseEvent& event)
{
  //  wxPostEvent(GetParent(),event);
  wxQueueEvent(GetParent(),event.Clone());
}


void MuniDetail::Assign(const FitsCoo& coo, const FitsValue& val)
{
  values = val;
  valchoice->SetStringSelection(values.Label_str(values.GetType()));

  coords = coo;
  coochoice->SetStringSelection(coords.Label_str(coords.GetType()));
}

void MuniDetail::OnChoiceVal(wxCommandEvent& event)
{
  for(int i = UNIT_COUNT; i <= UNIT_ERG; i++)
    if( event.GetString() == FitsValue::Label_str(i) ) {
      values.SetType((units_type)i);
      return;
    }
}

void MuniDetail::OnChoiceCoo(wxCommandEvent& event)
{
  for(int i = COO_PIXEL; i <= COO_EQSIX; i++)
    if( event.GetString() == FitsCoo::Label_str(i) ) {
      coords.SetType((coords_type)i);
      return;
    }
}


void MuniDetail::OnMouseMotion(MuniSlewEvent& event)
{
  int x = event.i_x;
  int y = event.i_y;

  // text
  values_str = values.Get_str(x,y);
  coords.GetStr(x,y,xcoo_str,ycoo_str);

  // image
  FitsBitmap picture(event.picture);
  if( !picture.IsOk()) return;
  int z = config->detail_scale;
  wxImage i(picture.GetWidth(),picture.GetHeight(),picture.NewTopsyTurvyRGB());
  if( i.IsOk() ) {
    i.Rescale(z*i.GetWidth(),z*i.GetHeight());
    zoom->SetImage(wxBitmap(i));
  }
}

void MuniDetail::OnUpdateValue(wxUpdateUIEvent& event)
{
  event.SetText(values_str);
}

void MuniDetail::OnUpdateXCoo(wxUpdateUIEvent& event)
{
  event.SetText(xcoo_str);
}

void MuniDetail::OnUpdateYCoo(wxUpdateUIEvent& event)
{
  event.SetText(ycoo_str);
}

void MuniDetail::OnUpdateXlabel(wxUpdateUIEvent& event)
{
  event.SetText(coords.GetType() == COO_PIXEL ? wxT("X:") : wxT("α:"));
  Layout();
}

void MuniDetail::OnUpdateYlabel(wxUpdateUIEvent& event)
{
  event.SetText(coords.GetType() == COO_PIXEL ? wxT("Y:") : wxT("δ:"));
  Layout();
}

